
#include <multimedia/Imagem.h>
#include <multimedia/FormatoBMP.h>


Imagem::Imagem(int cx, int cy, int x, int y)
    : Controle(cx, cy, x, y),
      Arquivo()
{
	m_pBandaR = new Sinal_2D(cx, cy);
	m_pBandaG = new Sinal_2D(cx, cy);
	m_pBandaB = new Sinal_2D(cx, cy);
	m_pBandaA = new Sinal_2D(cx, cy);
}

Imagem::Imagem(std::string strNomeDoArquivo, int x, int y)
    : Controle(0, 0, x, y),
      Arquivo()
{
    Abrir(strNomeDoArquivo);
}

Imagem::Imagem(WORD id, int x, int y)
    : Controle(0, 0, x, y),
      Arquivo()
{
    Abrir(id);
}

/*
Imagem::Imagem(Imagem& img)
    : Controle(img.Largura(), img.Altura(), img.PosicaoX(), img.PosicaoY()),
      Arquivo()
{
    *this = img;
}
*/

Imagem::~Imagem()
{
	delete m_pBandaR;
	delete m_pBandaG;
	delete m_pBandaB;
	delete m_pBandaA;
}

MatrizRGBA::ColunaRGBA& Imagem::operator[](int i) const
{
    return (Matriz())[i];
}

void Imagem::operator=(std::string strNomeDoArquivo)
{
    ;
}

void Imagem::operator=(const Imagem& img)
{
    Largura(img.Largura());
    Altura(img.Altura());
    
    for(int i=0; i<Largura(); i++)
        for(int j=0; j<Altura(); j++)
            (Matriz())[i][j] = img[i][j];
}

bool Imagem::Abrir()
{
    if(DlgAbrirArquivo("Bitmap (*.bmp)\0*.bmp\0\0", "bmp") == true)
    {
        if(this->Abrir(m_strNomeDoArquivo) == true)
            return true;
    }

    return false;
}

bool Imagem::Abrir(std::string strNomeDoArquivo, std::string strAlpha)
{
    bool ret = Abrir(strNomeDoArquivo);
    
	if(ret)
	{
		Imagem imAlpha;
		bool ret2 = imAlpha.Abrir(strAlpha);
		if(ret2)
		{
			for(int i=0; i<Largura(); i++)
				for(int j=0; j<Altura(); j++)
				{
					if(BANDA_R(imAlpha[i][j]) == 255)
					{
						int r = BANDA_R((*this)[i][j]);
						int g = BANDA_G((*this)[i][j]);
						int b = BANDA_B((*this)[i][j]);
						Matriz()[i][j] = RGBA(r,g,b,255);
					}
					else
					{
						Matriz()[i][j] = RGBA(0,0,0,0);
					}
				}
		}
	}

	return ret;
}

void Imagem::Abrir(WORD id, WORD idAlpha)
{
    Abrir(id);
    
    Imagem imAlpha;
    imAlpha.Abrir(idAlpha);
    for(int i=0; i<Largura(); i++)
        for(int j=0; j<Altura(); j++)
        {
            if(BANDA_R(imAlpha[i][j]) == 255)
            {
                int r = BANDA_R((*this)[i][j]);
                int g = BANDA_G((*this)[i][j]);
                int b = BANDA_B((*this)[i][j]);
                Matriz()[i][j] = RGBA(r,g,b,255);
            }
            else
            {
                Matriz()[i][j] = RGBA(0,0,0,0);
            }
        }
}

bool Imagem::Abrir(std::string strNomeDoArquivo)
{
    if(Arquivo::Abrir(strNomeDoArquivo) == true)
    {
        m_pContexto = FormatoBMP::Decodificar(m_pFluxo);
        m_uLargura = m_pContexto->Largura();
        m_uAltura  = m_pContexto->Altura();

        return true;
    }

    return false;
}

void Imagem::Abrir(WORD id)
{
    HBITMAP hBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(id));
    BITMAP bm;
    
    GetObject(hBitmap, sizeof(bm), &bm);
    
    m_uLargura = (unsigned short) bm.bmWidth;
    m_uAltura  = (unsigned short) bm.bmHeight;

    int padding = 0;
    while ( ((m_uLargura * 3 + padding) % 4) != 0)
        padding++;
    BITMAPINFO* lpbmi = new BITMAPINFO;
    lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    lpbmi->bmiHeader.biWidth = m_uLargura;
    lpbmi->bmiHeader.biHeight = m_uAltura;
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = 24; 
    lpbmi->bmiHeader.biCompression = 0; 
    lpbmi->bmiHeader.biSizeImage = m_uAltura * (m_uLargura * 3 + padding); 
    lpbmi->bmiHeader.biXPelsPerMeter = 0; 
    lpbmi->bmiHeader.biYPelsPerMeter = 0; 
    lpbmi->bmiHeader.biClrUsed = 0; 
    lpbmi->bmiHeader.biClrImportant = 0; 
    
    FluxoDeBytes* fluxo = new FluxoDeBytes(m_uAltura * (m_uLargura * 3 + padding));
    
    GetDIBits(GetDC(HWND_DESKTOP), hBitmap, 0, m_uAltura, fluxo->c_ptr(), lpbmi, DIB_RGB_COLORS);
    
    if(m_pContexto)
        delete m_pContexto;
    
    m_pContexto = new MatrizRGBA(m_uLargura, m_uAltura);
    
    int largura = m_uLargura  * 3;
    int altura  = m_uAltura   * 3;
    int larguraOcupada = (largura%4)? ((largura/4)+1)*4 : largura;
    int alturaOcupada  = altura;
        
    for(int j=m_pContexto->Altura()-1; j>=0; j--)
    {
        for(int i=0; i<m_pContexto->Largura(); i++)
        {
            register unsigned char bandaB = fluxo->PegarBytes(1);
            register unsigned char bandaG = fluxo->PegarBytes(1);
            register unsigned char bandaR = fluxo->PegarBytes(1);

            (*m_pContexto)[i][j] = RGBA(bandaR,bandaG,bandaB,255);
        }

        fluxo->PegarBytes(larguraOcupada-largura);
    }
}

#include <list>

bool Imagem::Salvar(std::string strNomeDoArquivo)
{
    FILE* arq = fopen(strNomeDoArquivo.c_str(), "wb");

    list<char> lista_antes;
    list<char> lista;
    list<char> lista_pos;
    list<char> lista_bmp;

    lista_antes.push_back('B');
    lista_antes.push_back('M');

    for(int i=0; i<4; i++)
        lista.push_back(0);

    lista.push_back(0x36);
    lista.push_back(0);
    lista.push_back(0);
    lista.push_back(0);

    lista.push_back(0x28);
    lista.push_back(0);
    lista.push_back(0);
    lista.push_back(0);

    //width
    lista.push_back(Largura()&0xFF);
    lista.push_back((Largura()>>8)&0xFF);
    lista.push_back((Largura()>>16)&0xFF);
    lista.push_back((Largura()>>24)&0xFF);

    //height
    lista.push_back(Altura()&0xFF);
    lista.push_back((Altura()>>8)&0xFF);
    lista.push_back((Altura()>>16)&0xFF);
    lista.push_back((Altura()>>24)&0xFF);

    lista.push_back(1);
    lista.push_back(0);

    lista.push_back(0x18);
    lista.push_back(0);

    //compression
    lista.push_back(0);
    lista.push_back(0);
    lista.push_back(0);
    lista.push_back(0);

    lista_pos.push_back(0x13);
    lista_pos.push_back(0x0B);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);

    lista_pos.push_back(0x13);
    lista_pos.push_back(0x0B);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);

    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);

    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);
    lista_pos.push_back(0x0);

    for(int j=0; j<Altura(); j++)
    {
        int i;

        for(i=0; i<Largura(); i++)
        {
            lista_bmp.push_back( BANDA_B(Matriz()[i][Altura()-j-1]) );
            lista_bmp.push_back( BANDA_G(Matriz()[i][Altura()-j-1]) );
            lista_bmp.push_back( BANDA_R(Matriz()[i][Altura()-j-1]) );
        }

        int align = Largura() % 4;
        for(i=0; i<align; i++)
            lista_bmp.push_back( 0 );
    }



    //write

    int size_file = lista_antes.size() + 4 + lista.size() + 4 + lista_pos.size() + lista_bmp.size();
    char* ar = (char*) ::malloc( size_file );
    int count = 0;

    for(std::list<char>::iterator it = lista_antes.begin(); it!=lista_antes.end(); it++)
        ar[count++] = *it;

    ar[count++] = size_file & 0xFF;
    ar[count++] = (size_file>>8) & 0xFF;
    ar[count++] = (size_file>>16) & 0xFF;
    ar[count++] = (size_file>>24) & 0xFF;

    for(std::list<char>::iterator it = lista.begin(); it!=lista.end(); it++)
        ar[count++] = *it;

    int size_bmp = lista_bmp.size();

    ar[count++] = size_bmp & 0xFF;
    ar[count++] = (size_bmp>>8) & 0xFF;
    ar[count++] = (size_bmp>>16) & 0xFF;
    ar[count++] = (size_bmp>>24) & 0xFF;

    for(std::list<char>::iterator it = lista_pos.begin(); it!=lista_pos.end(); it++)
        ar[count++] = *it;

    for(std::list<char>::iterator it = lista_bmp.begin(); it!=lista_bmp.end(); it++)
        ar[count++] = *it;

    fwrite(ar, 1, size_file, arq);

    fclose(arq);

    free(ar);
}

void Imagem::TonsDeCinza()
{
    for(int i=0; i<Largura(); i++)
        for(int j=0; j<Altura(); j++)
        {
            int r = BANDA_R(Matriz()[i][j]);
            int g = BANDA_G(Matriz()[i][j]);
            int b = BANDA_B(Matriz()[i][j]);
            int a = BANDA_A(Matriz()[i][j]);

            int c = (r+g+b)/3;

            Matriz()[i][j] = RGBA(c,c,c,a);
        }
}

void Imagem::Watershed()
{
    Pixel ***imi;
    Init(imi,Largura(),Altura());

    for(int i=0; i<Largura(); i++)
        for(int j=0; j<Altura(); j++)
            imi[i][j]->v = BANDA_R(Matriz()[i][j]);

    Pixel ***imo;
    Init(imo,Largura(),Altura());

    Pixel ***imd;
    Init(imd,Largura(),Altura());
    
    ///////////////
    Watershed_VS(imi, imo, imd, Largura(), Altura());
    ///////////////

    for(int i=1; i<Largura()-1; i++)
        for(int j=1; j<Altura()-1; j++)
            Matriz()[i][j] = imo[i][j]->v | 0xFF000000;

    Free(imi,Largura(),Altura());
    Free(imo,Largura(),Altura());
    Free(imd,Largura(),Altura());
}

void Imagem::WatershedLines()
{
    Pixel ***imi;
    Init(imi,Largura(),Altura());

    for(int i=0; i<Largura(); i++)
        for(int j=0; j<Altura(); j++)
            imi[i][j]->v = BANDA_R(Matriz()[i][j]);

    Pixel ***imo;
    Init(imo,Largura(),Altura());

    Pixel ***imd;
    Init(imd,Largura(),Altura());
    
    ///////////////
    Watershed_VS(imi, imo, imd, Largura(), Altura());
    ///////////////

    for(int i=1; i<Largura()-1; i++)
        for(int j=1; j<Altura()-1; j++)
        {
            if(imo[i][j]->v == 0)
                Matriz()[i][j] = RGBA(255,0,0,255);
            if(imo[i-1][j]->v != imo[i][j]->v && imo[i][j]->v == imo[i+1][j]->v &&
               imo[i-1][j]->v != 0 && imo[i][j]->v != 0 && imo[i+1][j]->v != 0)
                Matriz()[i][j] = RGBA(255,0,0,255);
            if(imo[i][j-1]->v != imo[i][j]->v && imo[i][j]->v == imo[i][j+1]->v &&
               imo[i][j-1]->v != 0 && imo[i][j]->v != 0 && imo[i][j+1]->v != 0)
                Matriz()[i][j] = RGBA(255,0,0,255);
        }

    Free(imi,Largura(),Altura());
    Free(imo,Largura(),Altura());
    Free(imd,Largura(),Altura());
}

void Imagem::Gaussian()
{
    ::GaussianFilter(*this);
}

void Imagem::Canny(int lower, int upper)
{
    ::CannyFilter(*this, lower, upper);
}

void Imagem::Linha(int x1, int y1, int x2, int y2, int valor)
{
    if(abs(x2-x1)>abs(y2-y1))
        if(x1==x2)
            for(int j=y1; y2>y1?j<=y2:j>=y2; y2>y1?j++:j--)
            {
                (*this)[x1][j] = valor;
            }
        else
            for(int i=x1; x2>x1?i<=x2:i>=x2; x2>x1?i++:i--)
            {
                double m = ((double)(y2-y1))/(x2-x1);
                int    j = Math::round(m*(i-x1)+y1);
            
                (*this)[i][j] = valor;
            }
    else
        if(y1==y2)
            for(int i=x1; x2>x1?i<=x2:i>=x2; x2>x1?i++:i--)
            {
                (*this)[i][y1] = valor;
            }
        else
            for(int j=y1; y2>y1?j<=y2:j>=y2; y2>y1?j++:j--)
            {
                double m = ((double)(x2-x1))/(y2-y1);
                int    i = Math::round(m*(j-y1)+x1);
            
                (*this)[i][j] = valor;
            }
}


void Imagem::Circulo(int x0, int y0, int raio, int valor)
{
    Matriz().Circulo(x0, y0, raio, valor);
}

void Imagem::CirculoComp(int x0, int y0, int raio, int valor)
{
    Matriz().CirculoComp(x0, y0, raio, valor);
}










void InterpolacaoBilinear(double **ar, long nTamanho, long nNovoTamanho)
{
    double *temp = (double*) malloc(nNovoTamanho*sizeof(double));
    double *ar_x = *ar;
    double f = ((double)nTamanho)/nNovoTamanho;
    double h = 0;
    int x;

	for(x=0; x<nNovoTamanho; x++)
    {
        double i = Math::floor(h);
        double j = i+1;
        double b = h-i;
        double a = 1-b;
        
        temp[x] = a*ar_x[(long)i] + b*ar_x[(long)j];
        
        h += f;
    }
    if(f<1.0)
        temp[x-1] = temp[x-2];
    
	free(*ar);
    *ar = temp;
}

void Imagem::RedimensionarHoriz(int cx)
{
    MatrizRGBA* mat = new MatrizRGBA(cx, Altura());

    double* tempR = (double*) malloc(Largura()*sizeof(double));
    double* tempG = (double*) malloc(Largura()*sizeof(double));
    double* tempB = (double*) malloc(Largura()*sizeof(double));
    double* tempA = (double*) malloc(Largura()*sizeof(double));

    for(int j=0; j<Altura(); j++)
    {
        
        for(int i=0; i<Largura(); i++)
        {
            tempR[i] = BANDA_R(Matriz()[i][j]);
            tempG[i] = BANDA_G(Matriz()[i][j]);
            tempB[i] = BANDA_B(Matriz()[i][j]);
            tempA[i] = BANDA_A(Matriz()[i][j]);
        }
        
        InterpolacaoBilinear(&tempR, Largura(), cx);
        InterpolacaoBilinear(&tempG, Largura(), cx);
        InterpolacaoBilinear(&tempB, Largura(), cx);
        InterpolacaoBilinear(&tempA, Largura(), cx);

        for(int i=0; i<cx; i++)
            (*mat)[i][j] = RGBA((int)tempR[i], (int)tempG[i], (int)tempB[i], (int)(tempA[i]));
            
    }
    free(tempR);
    free(tempG);
    free(tempB);
    free(tempA);
    
    delete m_pContexto;
    
    m_pContexto = mat;
    m_uLargura = cx;
}

void Imagem::RedimensionarVert(int cy)
{
    MatrizRGBA* mat = new MatrizRGBA(Largura(), cy);

    double* tempR = (double*) malloc(Altura()*sizeof(double));
    double* tempG = (double*) malloc(Altura()*sizeof(double));
    double* tempB = (double*) malloc(Altura()*sizeof(double));
    double* tempA = (double*) malloc(Altura()*sizeof(double));

	for(int i=0; i<Largura(); i++)
    {
        
        for(int j=0; j<Altura(); j++)
        {
            tempR[j] = BANDA_R(Matriz()[i][j]);
            tempG[j] = BANDA_G(Matriz()[i][j]);
            tempB[j] = BANDA_B(Matriz()[i][j]);
            tempA[j] = BANDA_A(Matriz()[i][j]);
        }
        
        InterpolacaoBilinear(&tempR, Altura(), cy);
        InterpolacaoBilinear(&tempG, Altura(), cy);
        InterpolacaoBilinear(&tempB, Altura(), cy);
        InterpolacaoBilinear(&tempA, Altura(), cy);

        for(int j=0; j<cy; j++)
            (*mat)[i][j] = RGBA((int)tempR[j], (int)tempG[j], (int)tempB[j], (int)(tempA[j]));
    }

    free(tempR);
    free(tempG);
    free(tempB);
    free(tempA);

    delete m_pContexto;
    
    m_pContexto = mat;
    m_uAltura = cy;
}

void Imagem::Redimensionar(int cx, int cy)
{
    RedimensionarHoriz(cx);
    RedimensionarVert(cy);
}

